package com.rc.evcharger.protocol.ylevcharger.netty.server;


import com.rc.evcharger.common.utils.NumberUtil;
import com.rc.mutievcharger.dto.ylevcharger.terminal.YlTerminalMessage;
import com.rc.mutievcharger.dto.ylevcharger.utils.YlMessageUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * netty消息接收器
 */
public class YlNettyServerHandler extends ChannelInboundHandlerAdapter {
    Logger logger = LoggerFactory.getLogger(YlNettyServerHandler.class);

    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    YlMessageHandler messageHandler = new YlMessageHandler();

    /**
     * 当我们的通道里有数据进行读取的时候触发的监听方法
     * 每当从客户端收到新的数据时， 这个方法会在收到消息时被调用， 这个例子中，收到的消息的类型是ByteBuf
     *
     * @param ctx 通道处理的上下文信息
     * @param msg 接收的消息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg){
        try {
            ByteBuf buf = (ByteBuf) msg;
            byte[] req = new byte[buf.readableBytes()];
            buf.readBytes(req);
            String body = NumberUtil.bin2HexStr(req);
            //关闭缓冲区;防止内存溢出
            buf.release();
//            body = messageHandler.dealMsg(ctx, body);
            String response = "";
            byte[] respTmp = null;

            if (StringUtils.isNotBlank(body)) {

                YlTerminalMessage message = YlMessageUtil.decoder(body);
                if (message == null) {
                    return;
                }

                Channel channel = ctx.channel();
                //处理各种消息
                response = messageHandler.doAction(message, channel.id().asLongText());
                respTmp = NumberUtil.hexStr2BinArr(response.replace(" ", ""));
                ByteBuf resp = Unpooled.copiedBuffer(respTmp);
                //ctx.write(resp);

                //回写给桩
                ctx.writeAndFlush(resp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
    }

    /**
     * ctx.write(Object)方法不会使消息写入到通道上， 他被缓冲在了内部，你需要调用ctx.flush()方法来把缓冲区中数据强行输出。
     * 或者你可以在channelRead方法中用更简洁的cxt.writeAndFlush(msg)以达到同样的目的
     *
     * @param ctx
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //logger.error("server channelReadComplete.");
        ctx.flush();// 刷新后才将数据发出到SocketChannel
    }

    /**
     * 这个方法会在发生异常时触发
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //logger.error("server exceptionCaught:", cause);
        // 发生异常后，退出登录，关闭连接

        handlerRemoved(ctx);
        //ctx.close();
    }

    /**
     * 当我们通道进行激活的时候触发的监听方法
     * 建立连接时保存 连接id 连接对象到内存中
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        String id = channel.id().asLongText();
        YlNettyChannelHandler nettyChannelHandler = YlNettyChannelHandler.newInstance();
        nettyChannelHandler.addChannel(id, channel);
        logger.warn("YLa new client connected 连接id:" + id);
    }

    /*
     * @see
     * io.netty.channel.ChannelHandlerAdapter#handlerRemoved(io.netty.channel.
     * ChannelHandlerContext)
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        YlNettyChannelHandler nettyChannelHandler = YlNettyChannelHandler.newInstance();
        Object value = nettyChannelHandler.getChannel(ctx.channel().id().asLongText());
        nettyChannelHandler.removeChannel(ctx.channel().id().asLongText());
        Channel channel = ctx.channel();
        if (value != null) {
            logger.warn(String.format("YLclient(%s) 端口准备登出", ctx.channel().localAddress().toString().substring(1)));
            messageHandler.logoutWithoutHearbeat(channel.id().asLongText());
            logger.warn("YLone client disconnected:");
        }
        if (channel != null) {
            channel.close();
            //ctx.close();
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // 两个心跳时间内没有收到数据就进行登出
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent ise = (IdleStateEvent) evt;
            if (ise.state().equals(IdleState.READER_IDLE)) {
                handlerRemoved(ctx);
            }
        }
    }
}